//
#include <g-bios.h>
#include <flash/flash.h>
#include <flash/nand.h>
#include <arch/s3c24xx.h>
#include <flash/nand_cmd.h>

#if 0
static struct NandOobLayout gS3cEccLayout = {
	.nEccCodeNum = 3,
	.piEccPos    = {0, 1, 2},
	.mFreeRegion = {{8, 8}}
};
#endif

static int S3C2440NandInit(struct NandCtrl *pNandCtrl)
{
	WriteShort(0x2440, NAND_CTRL_BASE + NF_CONF);
	WriteShort(0x1, NAND_CTRL_BASE + NF_CONT);

	return 0;
}

static void S3C2440NandCmdCtrl(struct NandFlash *pNandFlash, int cmd, UINT32 ulCtrl)
{
	if (cmd == NAND_CMMD_NONE)
		return;

	if (ulCtrl & NAND_CLE)
		WriteByte(cmd, NAND_CTRL_BASE + NF_CMMD);
	else
		WriteByte(cmd, NAND_CTRL_BASE + NF_ADDR);
}

static int S3C2440NandReady(struct NandFlash *pNandFlash)
{
	return ReadByte(NAND_CTRL_BASE + NF_STAT) & 0x01;
}

#if 0
static void  S3c2440WriteBuff(struct NandCtrl *pNandCtrl, const BYTE *pBuff, int nCount)
{
	int i;
	const UINT32 *p = (const UINT32 *)pBuff;

	for (i = 0; i < nCount; i += 4, p++)
		writel(*p, (void *)(NAND_CTRL_BASE + NF_DATA));
}

static void  S3c2440ReadBuff(struct NandCtrl *pNandCtrl, BYTE *pBuff, int nCount)
{
	int i;
	UINT32 *p = (UINT32 *)pBuff;

	for (i = 0; i < nCount; i += 4, p++)
		*p = readl((void *)(NAND_CTRL_BASE + NF_DATA));	
}

static void S3C2440NandEnableHWEcc(struct NandFlash *pNandFlash, int mode)
{
	UINT16 usCtl;

	usCtl = readw((void *)(NAND_CTRL_BASE + NF_CONF));
	usCtl |= (1 << 12);
	writew(usCtl, (void *)(NAND_CTRL_BASE + NF_CONF));
}


static int S3C2440NandCalculateHWEcc(struct NandFlash *pNandFlash, const UINT8 *pbData, UINT8 *pbEccCode)
{
//	pbEccCode[0] = ReadByte((void *)(NAND_CTRL_BASE + NF_ECC + 0));
//	pbEccCode[1] = ReadByte((void *)(NAND_CTRL_BASE + NF_ECC + 1));
//	pbEccCode[2] = ReadByte((void *)(NAND_CTRL_BASE + NF_ECC + 2));

	return 0;
}


static int S3C2440NandCorrectData(struct NandFlash *pNandFlash,
										UINT8 *pbData,
										UINT8 *pbReadEcc,
										UINT8 *pbCalcEcc
										)
{
	UINT32 diff0, diff1, diff2;
	UINT32 bit, byte;


	diff0 = pbReadEcc[0] ^ pbCalcEcc[0];
	diff1 = pbReadEcc[1] ^ pbCalcEcc[1];
	diff2 = pbReadEcc[2] ^ pbCalcEcc[2];

	if (diff0 == 0 && diff1 == 0 && diff2 == 0)
	{
		return 0;
	}

	// TODO:  add data correction code here

	return -1;
}
#endif


static __INIT__ int S3C2440NandProbe(void)
{
	int ret;
	struct NandCtrl *pNandCtrl;
	ECC_MODE eOldMode; 
	UINT16 wVal;

	printf("%s():\n", __FUNCTION__);

	pNandCtrl = GkNandCtrlNew();

	if (NULL == pNandCtrl)
	{
		return -ENOMEM;
	}

	pNandCtrl->pReadDataAddr  = (void *)(NAND_CTRL_BASE + NF_DATA);
	pNandCtrl->pWriteDataAddr = (void *)(NAND_CTRL_BASE + NF_DATA);

	strcpy(pNandCtrl->szNcName, "s3c24xx_nand");

#if 0
	pNandCtrl->pHardOobLayout = &gS3cEccLayout;
	pNandCtrl->nEccDataLen    = 512;
	pNandCtrl->nEccCodeNum    = 3;
#endif

	pNandCtrl->CmdCtrl  	  = S3C2440NandCmdCtrl;
	pNandCtrl->FlashIsReady   = S3C2440NandReady;
#if 0
	pNandCtrl->RawRead        = S3c2440ReadBuff;
	pNandCtrl->RawWrite       = S3c2440WriteBuff;
	
	pNandCtrl->EccEnable      = S3C2440NandEnableHWEcc;
	pNandCtrl->EccGenerate    = S3C2440NandCalculateHWEcc;
	pNandCtrl->EccCorrect     = S3C2440NandCorrectData;
	
#endif
	// GkNandSetEccMode(pNandCtrl, NAND_ECC_HW, &eOldMode);

	S3C2440NandInit(pNandCtrl);

	ret = GkNandCtrlRegister(pNandCtrl);
	if (ret < 0) 
	{
		ret = -ENODEV;
		goto L1;
	}

	return 0;
L1:
	// S3C2440NandDisable(pNandCtrl);
	free(pNandCtrl);
	return ret;
}

DRIVER_INIT(S3C2440NandProbe);

